#pragma once
// -------------------------------------------------
// ZED Kit
// -------------------------------------------------
//   File Name: registry.hpp
//      Author: Ziming Li
//     Created: 2022-10-03
// -------------------------------------------------
// Copyright (C) 2022 MingYang Software Technology.
// -------------------------------------------------

#ifndef ZED_WIN_REGISTRY_HPP
#define ZED_WIN_REGISTRY_HPP

#include <string>
#include "../platform_sdk.h"
#include "./handled_resource.hpp"

namespace zed {

class reg_key : public unique_resource<HKEY, default_resource_finalizer<HKEY>, reg_key_traits>
{
    using base_class = unique_resource<HKEY, default_resource_finalizer<HKEY>, reg_key_traits>;
public:
    static reg_key open(HKEY h, PCSTR sub_key, REGSAM sam_desired, LSTATUS *status = nullptr);
    static reg_key open(HKEY h, PCWSTR sub_key, REGSAM sam_desired, LSTATUS *status = nullptr);

    template <typename CharT>
    bool query_value(const CharT *name, DWORD &dst, LSTATUS *status = nullptr);
    template <typename CharT>
    bool query_value(const CharT *name, std::basic_string<CharT> &dst, LSTATUS *status = nullptr);
private:
    reg_key(HKEY hk) : base_class(hk) {}

    LSTATUS query_value(PCSTR name, LPBYTE dst, LPDWORD cb);
    LSTATUS query_value(PCWSTR name, LPBYTE dst, LPDWORD cb);

    static constexpr DWORD BUF_SIZE = 128;
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

inline reg_key reg_key::open(HKEY h, PCSTR sub_key, REGSAM sam_desired, LSTATUS *status)
{
    HKEY ret = nullptr;
    LSTATUS l = ::RegOpenKeyExA(h, sub_key, 0, sam_desired, &ret);
    if (nullptr != status)
        *status = l;
    return reg_key(ret);
}

inline reg_key reg_key::open(HKEY h, PCWSTR sub_key, REGSAM sam_desired, LSTATUS *status)
{
    HKEY ret = nullptr;
    LSTATUS l = ::RegOpenKeyExW(h, sub_key, 0, sam_desired, &ret);
    if (nullptr != status)
        *status = l;
    return reg_key(ret);
}

inline LSTATUS reg_key::query_value(PCSTR name, LPBYTE dst, LPDWORD cb)
{
    return ::RegQueryValueExA(get(), name, nullptr, nullptr, dst, cb);
}

inline LSTATUS reg_key::query_value(PCWSTR name, LPBYTE dst, LPDWORD cb)
{
    return ::RegQueryValueExW(get(), name, nullptr, nullptr, dst, cb);
}

template <typename CharT>
bool reg_key::query_value(const CharT *name, DWORD &dst, LSTATUS *status)
{
    DWORD cb = sizeof(DWORD);
    LSTATUS l = query_value(name, reinterpret_cast<LPBYTE>(&dst), &cb);
    if (nullptr != status)
        *status = l;
    return ERROR_SUCCESS == l;
}

template <typename CharT>
bool reg_key::query_value(const CharT *name, std::basic_string<CharT> &dst, LSTATUS *status)
{
    dst.resize(BUF_SIZE);

    DWORD cb = (BUF_SIZE + 1) * sizeof(CharT);
    LSTATUS l = query_value(name, reinterpret_cast<LPBYTE>(const_cast<CharT *>(dst.c_str())), &cb);
    if (ERROR_SUCCESS == l)
    {
        if (cb > 0)
            dst.resize(cb / sizeof(CharT) - 1);
        else
            dst.clear();
    }
    else if (ERROR_MORE_DATA == l)
    {
        dst.resize(cb / sizeof(CharT) - 1);
        l = query_value(name, reinterpret_cast<LPBYTE>(const_cast<CharT *>(dst.c_str())), &cb);
    }
    else
    {
        dst.clear();
    }

    if (nullptr != status)
        *status = l;
    return ERROR_SUCCESS == l;
}

} // namespace zed

#endif // ZED_WIN_REGISTRY_HPP
